// Written by Sarah Thompson (sarah@telergy.com) 2002.
// Refactored by Fredrique Samuels (fredriquesamuels@gmail.com)
//
// License: Public domain. You are free to use this code however you like, with the proviso that
//          the author(s) takes on no responsibility or liability for any use.


#ifndef SIGSLOT_BASE_H__
#define SIGSLOT_BASE_H__

#include <set>
#include <list>

#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS))
#	define _SIGSLOT_SINGLE_THREADED
#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
#	define _SIGSLOT_HAS_POSIX_THREADS
#	include <pthread.h>
#elif defined(WIN32)
#	define _SIGSLOT_HAS_WIN32_THREADS
#	include <windows.h>
#else
#	define _SIGSLOT_SINGLE_THREADED
#endif

#ifndef SIGSLOT_DEFAULT_MT_POLICY
#	ifdef _SIGSLOT_SINGLE_THREADED
#		define SIGSLOT_DEFAULT_MT_POLICY single_threaded
#	else
#		define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
#	endif
#endif

namespace sigslot {

class single_threaded {
public:
	single_threaded() {

	}

	virtual ~single_threaded() {

	}

	virtual void lock() {

	}

	virtual void unlock() {

	}
};

#ifdef _SIGSLOT_HAS_WIN32_THREADS
// The multi threading policies only get compiled in if they are enabled.
class multi_threaded_global {
public:
	multi_threaded_global() {
		static bool isinitialised = false;

		if (!isinitialised) {
			InitializeCriticalSection(get_critsec());
			isinitialised = true;
		}
	}

	multi_threaded_global(const multi_threaded_global&) {

	}

	virtual ~multi_threaded_global() {

	}

	virtual void lock() {
		EnterCriticalSection(get_critsec());
	}

	virtual void unlock() {
		LeaveCriticalSection(get_critsec());
	}

private:
	CRITICAL_SECTION* get_critsec() {
		static CRITICAL_SECTION g_critsec;
		return &g_critsec;
	}
};

class multi_threaded_local {
public:
	multi_threaded_local() {
		InitializeCriticalSection(&m_critsec);
	}

	multi_threaded_local(const multi_threaded_local&) {
		InitializeCriticalSection(&m_critsec);
	}

	virtual ~multi_threaded_local() {
		DeleteCriticalSection(&m_critsec);
	}

	virtual void lock() {
		EnterCriticalSection(&m_critsec);
	}

	virtual void unlock() {
		LeaveCriticalSection(&m_critsec);
	}

private:
	CRITICAL_SECTION m_critsec;
};
#endif // _SIGSLOT_HAS_WIN32_THREADS
#ifdef _SIGSLOT_HAS_POSIX_THREADS
// The multi threading policies only get compiled in if they are enabled.
class multi_threaded_global
{
public:
	multi_threaded_global()
	{
		pthread_mutex_init(get_mutex(), NULL);
	}

	multi_threaded_global(const multi_threaded_global&)
	{

	}

	virtual ~multi_threaded_global()
	{

	}

	virtual void lock()
	{
		pthread_mutex_lock(get_mutex());
	}

	virtual void unlock()
	{
		pthread_mutex_unlock(get_mutex());
	}

private:
	pthread_mutex_t* get_mutex()
	{
		static pthread_mutex_t g_mutex;
		return &g_mutex;
	}
};

class multi_threaded_local
{
public:
	multi_threaded_local()
	{
		pthread_mutex_init(&m_mutex, NULL);
	}

	multi_threaded_local(const multi_threaded_local&)
	{
		pthread_mutex_init(&m_mutex, NULL);
	}

	virtual ~multi_threaded_local()
	{
		pthread_mutex_destroy(&m_mutex);
	}

	virtual void lock()
	{
		pthread_mutex_lock(&m_mutex);
	}

	virtual void unlock()
	{
		pthread_mutex_unlock(&m_mutex);
	}

private:
	pthread_mutex_t m_mutex;
};
#endif // _SIGSLOT_HAS_POSIX_THREADS
template<class mt_policy> class lock_block {
public:
	mt_policy *m_mutex;

	lock_block(mt_policy *mtx) :
		m_mutex(mtx) {
		m_mutex->lock();
	}

	~lock_block() {
		m_mutex->unlock();
	}
};

template<class mt_policy> 
class has_slots;

class _signal_interface {

};


class connection_interface {

};

template<class mt_policy=SIGSLOT_DEFAULT_MT_POLICY> 
class _signal_base : public mt_policy, public _signal_interface {
public:
	virtual void disconnect(has_slots<mt_policy>* pslot) = 0;
};

template<class mt_policy=SIGSLOT_DEFAULT_MT_POLICY> class has_slots :
	public mt_policy {
private:
	typedef std::set<_signal_interface*> sender_set;
	typedef sender_set::const_iterator const_iterator;
public:
	has_slots() {

	}

	virtual ~has_slots() {
		disconnect_all();
	}

	inline void signal_connect(_signal_interface* sender) {
		lock_block<mt_policy> lock(this);
		m_senders.insert(sender);
	}

	inline void signal_disconnect(_signal_interface* sender) {
		lock_block<mt_policy> lock(this);
		m_senders.erase(sender);
	}

	inline void disconnect_all() {
		lock_block<mt_policy> lock(this);
		const_iterator it = m_senders.begin();
		const_iterator itEnd = m_senders.end();

		while (it != itEnd) {
			((_signal_base<mt_policy>*)(*it))->disconnect(this);
			++it;
		}

		m_senders.erase(m_senders.begin(), m_senders.end());
	}
private:
	sender_set m_senders;
};

}// namespace sigslot

#endif // SIGSLOT_H__
